
include("shared.lua")
include("entities/base_wire_entity/init.lua")
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")

local NULLVEC=Vector(0,0,0)

ENT.IgnoreDamage	= true
ENT.wac_ignore		= true
ENT.WheelInfo		={}

ENT.UsePhysRotor	= true
ENT.Submersible	= false
ENT.CrRotorWash	= true
ENT.RotorWidth	= 200
ENT.TopRotorDir	= 1
ENT.BackRotorDir	= 1
ENT.TopRotorPos	= Vector(0,0,50)
ENT.BackRotorPos	= Vector(-185,-3,13)
ENT.EngineForce	= 20
ENT.BrakeMul		= 1
ENT.AngBrakeMul	= 0.01
ENT.Weight		= 1000
ENT.SeatSwitcherPos= Vector(0,0,50)
ENT.BullsEyePos	= Vector(20,0,50)
ENT.MaxEnterDistance= 50
ENT.WheelStabilize	=-400
ENT.HatingNPCs={
	"npc_strider",
	"npc_combinegunship",
	"npc_combinedropship",
	"npc_helicopter",
	"npc_hunter",
	"npc_ministrider",
	"npc_turret_ceiling",
	"npc_turret_floor",
	"npc_turret_ground",
	"npc_rollermine",
	"npc_sniper",
}

function ENT:Initialize()
	self.Entity:SetModel(self.Model)
	self.Entity:PhysicsInit(SOLID_VPHYSICS)
	self.Entity:SetMoveType(MOVETYPE_VPHYSICS)
	self.Entity:SetSolid(SOLID_VPHYSICS)
	self.Phys = self.Entity:GetPhysicsObject()
	if self.Phys:IsValid() then
		self.Phys:SetMass(self.Weight)
		self.Phys:Wake()
	end
	
	self.OnRemoveEntities={}
	self.OnRemoveFunctions={}
	self.Wheels={}
	
	self.SeatsT=self.SeatsT or table.Copy(self:AddSeatTable())
	self:AddRotor()
	self:AddSounds()
	self:AddSeats()
	self:AddWheels()
	self:AddStuff()
	self:AddBullsEye()
	self:AddWireInputs()
	self:AddAirResistanceMods()
	
	self.UpdateSecond = 0
	self.LastDamageTaken=0
	self.wac_seatswitch = true
	self.StartTime = 0
	self.HoverTime=0
	self.engineHealth = 100
	self.pitchMul = 0
	self.yawMul = 0
	self.rollMul = 0
	self.startMul = 0
	self.upMul = 0
	self:SetNWFloat("health", 100)
	self.LastActivated = 0
	self.NextWepSwitch = 0
	self.NextCamSwitch = 0
	self.engineStart = 0
	self.LastPhys=0
	self.Passenger={}
end

function ENT:AddAirResistanceMods()
	self.AirResistanceMods={
		[1]=Vector(0,0,0),				--avel=lvel.x*this
		[2]=Vector(-0.005,0,0.0015),	--avel=lvel.y*this
		[3]=Vector(0,-0.00005,0),		--avel=lvel.z*this
		[4]=Vector(0,0.003,0.003),		--general local air resistance
	}
end

function ENT:PhysicsUpdate(ph)
    if self.LastPhys==CurTime() then return end
    local vel = ph:GetVelocity()    
    local pos=self:GetPos()
    local ri=self:GetRight()
    local up=self:GetForward()+self:GetUp()*1
    local fwd=self:GetUp()
    local ang=self:GetAngles()
    local dvel=vel:Length()
    local lvel=self:WorldToLocal(pos+vel)
    if !self.Lock then
        local realism=2
        if self.Passenger[1] and self.Passenger[1]:IsValid() and self.Passenger[1]:IsPlayer() then
            self.yawMul = self:GetPLControl(self.Passenger[1], WAC_HELI_LEANP, self.yawMul)
            self.rollMul     = self.BackRotor and self:GetPLControl(self.Passenger[1], WAC_HELI_LEANY, self.rollMul) or 0
            self.pitchMul    = self:GetPLControl(self.Passenger[1], WAC_HELI_LEANR, self.pitchMul)
            self.upMul     = self:GetPLControl(self.Passenger[1], WAC_HELI_UPDOWN, self.upMul, true)
            realism=math.Clamp(tonumber(self.Passenger[1]:GetInfo("wac_cl_heli_realism")),1,3)
        else
            self.yawMul=0
            self.rollMul=0
            self.pitchMul=0
        end
        local angbrake=((self.TopRotor and self.BackRotor) and ph:GetAngleVelocity()*self.AngBrakeMul/math.pow(realism,2)*9 or NULLVEC)
        local t=self:CalculateHover(ph,pos,vel,ang)
        
        local phm=WAC.Helicopter.CVars.PHM:GetFloat()
        if self.UsePhysRotor then
            if self.TopRotor and self.TopRotor.Phys and self.TopRotor.Phys:IsValid() then
                if self.RotorBlurModel then
                    self.TopRotorModel:SetColor(255,255,255,math.Clamp(1.3-self.startMul,0.1,1)*255)
                end
                
                local downvel = self.TopRotor:WorldToLocal(self.TopRotor:GetVelocity()+self.TopRotor:GetPos()).z
                local brake = (math.abs(self.pitchMul)+math.abs(self.yawMul)+math.abs(self.rollMul))*0.01
                brake=brake+downvel*(self.upMul+1)*self.startMul/900+math.Clamp(math.abs(self.TopRotor.Phys:GetAngleVelocity().z/100),0,1)--self.upMul*self.startMul/3
                self.TopRotor.Phys:AddAngleVelocity(Vector(-brake,0,0)*self.TopRotorDir)
                
                self.startMul = math.Clamp(self.TopRotor.Phys:GetAngleVelocity().x/2600*self.TopRotorDir*phm,-1,1)
                if self.Active and self.TopRotor:WaterLevel() <= 0 then
                    self.engineStart=math.Clamp(self.engineStart+FrameTime()*0.1*WAC.Helicopter.CVars.SSpeed:GetFloat(),0,1)
                    self.TopRotor.Phys:AddAngleVelocity(Vector(math.pow(self.engineStart,2)*164,0,0)*self.TopRotorDir)
                else
                    self.engineStart = math.Clamp(self.engineStart-FrameTime()*0.16*WAC.Helicopter.CVars.SSpeed:GetFloat(), 0, 1)
                end
                local mind = (100-self.TopRotor.fHealth)/100
                ph:AddAngleVelocity(VectorRand()*self.startMul*mind*phm)
                if self.BackRotor and self.BackRotor:IsValid() and self.BackRotor.Phys:IsValid() then
                    self.BackRotor.Phys:AddAngleVelocity(Vector(self.startMul*300*self.BackRotorDir-self.BackRotor.Phys:GetAngleVelocity().x/10,0,0))
                else
                    ph:AddAngleVelocity(Vector(0,0,0-self.startMul*self.TopRotorDir)+ph:GetAngleVelocity()*0.01*phm)
                    ph:AddAngleVelocity(VectorRand()*self.startMul*mind*phm)
                    if !self.Sound.CrashAlarm:IsPlaying() then
                        self.Sound.CrashAlarm:Play()
                    end
                end
				
				local speed = self:GetVelocity():Length()
				
				if speed < 1000 and self.engineStart > 0.5 then
					local temp=vel+up*(self.upMul*self.startMul*self.EngineForce/100+self.startMul*5)*phm
					temp=temp-self:LocalToWorld(lvel*self.AirResistanceMods[4]*dvel/1000)+pos
					ph:SetVelocity(temp)
				elseif speed > 2000 then				
				elseif self.engineStart > 0.5 then
					local temp=vel+up*(self.upMul*self.startMul*1.7*self.EngineForce/25+self.startMul*9)*phm
					temp=temp-self:LocalToWorld(lvel*self.AirResistanceMods[4]*dvel/1000)+pos
					ph:SetVelocity(temp)
				else
					local temp=vel+up*(self.upMul*self.startMul*1.7*self.EngineForce/25)*phm
					temp=temp-self:LocalToWorld(lvel*self.AirResistanceMods[4]*dvel/1000)+pos
					ph:SetVelocity(temp)
				end
                
                for _,e in pairs( self.Wheels ) do
                    if ValidEntity(e) then
                        local ph=e:GetPhysicsObject()
                        if ph:IsValid() then
                            local lpos=self:WorldToLocal(e:GetPos())
                            e:GetPhysicsObject():AddVelocity(Vector(0,0,9)+self:LocalToWorld(Vector(0,0,lpos.y*(self.pitchMul+t.r)/math.pow(realism,1.3)*4.17-lpos.y*angbrake.x)/4)-pos)
                            e:GetPhysicsObject():AddVelocity(up*ang.r*lpos.y/self.WheelStabilize)
                        end
                    end
                end
                
            elseif self.BackRotor and self.BackRotor:IsValid() and self.BackRotor.Phys:IsValid() then
                ph:AddAngleVelocity((ph:GetAngleVelocity()*0.01+Vector(self.BackRotor.Phys:GetAngleVelocity().y/300,0,0))*self.startMul)
            end
        else
            self.startMul=math.Approach(self.startMul, self.Active and 1 or 0, self.EngineForce/1000)
            ph:SetVelocity(vel*0.999+(up*self.startMul*(self.upMul+1)*7 + (fwd*math.Clamp(ang.p*0.1, -2, 2) + ri*math.Clamp(ang.r*0.1, -2, 2))*self.startMul)*phm)
        end

        ph:AddAngleVelocity((Vector((self.pitchMul*1.5+t.r)*self.startMul, (self.yawMul+t.p)*self.startMul, self.rollMul*1.5*self.startMul)/math.pow(realism,1.3)*4.17-angbrake+(lvel.x*self.AirResistanceMods[1]+lvel.y*self.AirResistanceMods[2]+lvel.z*self.AirResistanceMods[3])*dvel/1000)*phm)
        for k,s in pairs(self.SeatsT) do
            if s.wep[s.wep_act].Phys and ValidEntity(self.Passenger[k]) then
                s.wep[s.wep_act].Phys(self,s.wep[s.wep_act],self.Passenger[k])
            end
        end
    end
    if self.CustomPhysicsUpdate then self:CustomPhysicsUpdate(ph) end
    self.LastPhys=CurTime()
end
function ENT:AddRotor()
    if self.UsePhysRotor then
        self.TopRotor = ents.Create("prop_physics")
        self.TopRotor:SetModel("models/props_junk/sawblade001a.mdl")
        self.TopRotor:SetPos(self:LocalToWorld(self.TopRotorPos))
        self.TopRotor:SetAngles(self:GetAngles())
        self.TopRotor:SetOwner(self.Owner)
        self.TopRotor:Spawn()
        self.TopRotor:SetNotSolid(true)
        self.TopRotor.Phys = self.TopRotor:GetPhysicsObject()
        self.TopRotor.Phys:EnableGravity(false)
        self.TopRotor.Phys:SetMass(5)
        self.TopRotor.Phys:EnableDrag(false)
        self.TopRotor:SetColor(Color(0,0,0,0))
        self.TopRotor.fHealth = 100
        self.TopRotor.wac_ignore = true
        if self.RotorModel then
            local e = ents.Create("wac_hitdetector")
            e:SetModel(self.RotorModel)
            e:SetPos(self:LocalToWorld(self.TopRotorPos))
            e:SetAngles(self:GetAngles())
            e.TouchFunc=function(e)
                local ph=e:GetPhysicsObject()
                if ph:IsValid() then
                    local pass=true
                    for k,p in pairs(self.Passenger) do
                        if p==e then pass=false end
                    end
                    if pass and e != self and e != self.TopRotor and e != self.BackRotor and !string.find(e:GetClass(), "func*") and ValidEntity(self.TopRotor) then
                        local dmg=(self.TopRotor:GetPhysicsObject():GetAngleVelocity():Length()+ph:GetVelocity():Length())/100
                        ph:AddVelocity((e:GetPos()-self.TopRotor:GetPos())*dmg/e:GetPhysicsObject():GetMass()*10)
                        self:DamageBigRotor(dmg)
                        e.Entity:TakeDamage(dmg, ValidEntity(self.Passenger[1]) and self.Passenger[1] or self.Entity, self.Entity)
                    end
                end
            end
            e:Spawn()
            e:SetOwner(self.Owner)
            e:SetParent(self.TopRotor)
            e.wac_ignore = true
            local obb=e:OBBMaxs()
            self.RotorWidth=(obb.x>obb.y and obb.x or obb.y)
            self.RotorHeight=obb.z
            self.TopRotorModel=e
        end
        self.BackRotor=self:AddBackRotor()
        self:SetNWEntity("rotor_rear",self.BackRotor)
        constraint.Axis(self.Entity, self.TopRotor, 0, 0, self.TopRotorPos, Vector(1,0,0),0,0,0.01,1)    
        constraint.Axis(self.Entity, self.BackRotor, 0, 0, self.BackRotorPos, Vector(1,0,0),0,0,0.01,1)
        constraint.NoCollide(self.TopRotor, self.BackRotor, 0, 0)
        self:AddOnRemove(self.TopRotor)
        self:AddOnRemove(self.BackRotor)
    end
end

function ENT:AddBackRotor()
    local e=ents.Create("prop_physics")
    e:SetAngles(self:GetAngles())
    e:SetPos(self:LocalToWorld(self.BackRotorPos))
    e:SetModel(self.BackRotorModel)
    e:Spawn()
    e:SetOwner(self.Owner)
    e:SetNWFloat("rotorhealth", 100)
    e.wac_ignore=true
    e.Phys=e:GetPhysicsObject()
    if e.Phys:IsValid() then
        e.Phys:Wake()
        e.Phys:EnableGravity(false)
        e.Phys:EnableDrag(false)
        e.Phys:SetMass(10)
    end
    e.fHealth = 100
    return e
end

function ENT:UpdateTransmitState() return TRANSMIT_ALWAYS end

function ENT:AddWireInputs()
	if Wire_CreateInputs then
		Wire_CreateInputs(self,{"Pitch","Yaw","Roll","Start","Thrust","Fire","NextWeapon"})
	end
end

function ENT:TriggerInput(n,v)
	if n=="Pitch" then
		--self.yawMul
	end
end

function ENT:AddBullsEye()
	self.BullsEye=ents.Create("npc_bullseye")
	self.BullsEye:SetPos(self:LocalToWorld(self.BullsEyePos))
	self.BullsEye:SetParent(self.Entity)
	self.BullsEye:SetKeyValue("health","9999")
	self.BullsEye:SetKeyValue("spawnflags","256")
	self.BullsEye:SetNotSolid(true)
	self.BullsEye:Spawn()  
	self.BullsEye:Activate()
	for _,s in pairs(self.HatingNPCs) do
		self.BullsEye:Fire("SetRelationShip", s.." D_HT 60")
	end
end

function ENT:AddStuff() end

function ENT:AddSeats()
	self.Seats={}
	local e=ents.Create("wac_v_connector")
	e:SetPos(self:LocalToWorld(self.SeatSwitcherPos))
	e:SetColor(0,0,0,0)
	e:Spawn()
	e.wac_ignore = true
	e:SetNotSolid(true)
	e:SetParent(self.Entity)
	self.SeatSwitcher=e
	for k,v in pairs(self.SeatsT) do
		local ang=self:GetAngles()
		v.wep_act=1
		v.wep_next=0
		for i,t in pairs(v.wep) do
			if type(t)=="table" then
				if t.Init then t.Init(self,t) end
				self:SetNWInt("seat_"..k.."_"..i.."_ammo",t.Ammo)
				self:SetNWInt("seat_"..k.."_"..i.."_nextshot",1)
				self:SetNWInt("seat_"..k.."_"..i.."_lastshot",0)
			else
				t=nil
			end
		end
		self:SetNWInt("seat_"..k.."_actwep", 1)
		self.Seats[k]=ents.Create("prop_vehicle_prisoner_pod")
		self.Seats[k]:SetModel("models/nova/airboat_seat.mdl") 
		self.Seats[k]:SetPos(self:LocalToWorld(v.Pos))
		if v.Ang then
			local a=self:GetAngles()
			a.y=a.y-90
			a:RotateAroundAxis(Vector(0,0,1),v.Ang.y)
			self.Seats[k]:SetAngles(a)
		else
			ang:RotateAroundAxis(self:GetUp(),-90)
			self.Seats[k]:SetAngles(ang)
		end
		self.Seats[k]:Spawn()
		self.Seats[k]:SetColor(0,0,0,0)
		self.Seats[k].BigOne = self.Entity
		self.Seats[k].Phys = self.Seats[k]:GetPhysicsObject()
		self.Seats[k].Phys:EnableGravity(true)
		self.Seats[k].Phys:SetMass(1)
		self.Seats[k]:SetNotSolid(true)
		self.Seats[k]:SetParent(self.Entity)
		self.Seats[k].wac_ignore = true
		self.Seats[k]:SetNWEntity("BigOne", self.Entity)
		self.Seats[k]:SetKeyValue("limitview","0")
		self.SeatSwitcher:AddVehicle(self.Seats[k])
		self:AddOnRemove(self.Seats[k])
	end
end

function ENT:AddWheels()
	for _,t in pairs(self.WheelInfo) do
		if t.mdl then
			local e=ents.Create("prop_physics")
			e:SetModel(t.mdl)
			e:SetPos(self:LocalToWorld(t.pos))
			e:SetAngles(self:GetAngles())
			e:Spawn()
			e:Activate()
			local ph=e:GetPhysicsObject()
			if ph:IsValid() then
				if t.mass then
					ph:SetMass(math.Clamp(t.mass,1,5000))
				end
				ph:EnableDrag(false)
			else
				e:Remove()
			end
			constraint.Axis(e,self,0,0,Vector(0,0,0),self:WorldToLocal(e:LocalToWorld(Vector(0,1,0))),0,0,t.friction,1)
			table.insert(self.Wheels,e)
			self:AddOnRemove(e)
		end
	end
end

function ENT:NextWeapon(t,k,p)
	if t.wep[t.wep_act].DeSelect then t.wep[t.wep_act].DeSelect(self.Entity, t.wep[t.wep_act], p) end
	t.wep_act=(t.wep_act<#t.wep)and(t.wep_act+1)or(1)
	t.wep_next=CurTime()+0.5
	self:SetNWInt("seat_"..k.."_actwep", t.wep_act)
end

function ENT:SeatSwitch(p, s)
	if !self.Seats[s] then return end
	local psngr = self.Seats[s]:GetPassenger()
	if !psngr or !psngr:IsValid() or !psngr:InVehicle() then
		p:ExitVehicle()
		p:EnterVehicle(self.Seats[s])
		self:UpdateSeats()
	end
end

function ENT:EjectPassenger(ply,idx,t)
	if ply.LastVehicleEntered and ply.LastVehicleEntered<CurTime() then
		if !idx then
			for k,p in pairs(self.Passenger) do
				if p==ply then idx=k end
			end
			if !idx then
				return
			end
		end
		ply.LastVehicleEntered = CurTime()+0.5
		ply:ExitVehicle()
		ply:SetPos(self:LocalToWorld(self.SeatsT[idx].ExitPos))
		ply:SetVelocity(self:GetPhysicsObject():GetVelocity()*0.8)
		ply:SnapEyeAngles((self:LocalToWorld(self.SeatsT[idx].Pos-Vector(0,0,40))-ply:GetPos()):Angle())
		self:UpdateSeats()
	end
end

function ENT:Use(act, cal)
	if self.Lock then return end
	local crt = CurTime()
	if !act.LastVehicleEntered or act.LastVehicleEntered < crt then
		local d=self.MaxEnterDistance
		local v
		for k,veh in pairs(self.Seats) do
			if veh and veh:IsValid() then
				local psngr = veh:GetPassenger()
				if !psngr or !psngr:IsValid() then
					local dist=veh:GetPos():Distance(util.QuickTrace(act:GetShootPos(),act:GetAimVector()*self.MaxEnterDistance,act).HitPos)
					if dist<d then
						d=dist
						v=veh
					end
				end
			end
		end
		if v then
			act.HeliKeysDown={}
			act:EnterVehicle(v)
			act.LastVehicleEntered=crt+0.5		
		end
	end
	self:UpdateSeats()
end

function ENT:UpdateSeats()
	for k, veh in pairs(self.Seats) do
		if !veh:IsValid() then return end
		local p = veh:GetPassenger()
		if self.Passenger[k] != p then
			if ValidEntity(self.Passenger[k]) then
				self.Passenger[k].HeliKeysDown={}
				self.Passenger[k]:SetNWEntity("BigOne", NULL)
				local t=self.SeatsT[k].wep[self.SeatsT[k].wep_act]
				if t and t.DeSelect then
					t.DeSelect(self,t,self.Passenger[k])
				end
			end
			self:SetNWEntity("passenger_"..k, p)
			p:SetNWInt("wac_passenger_id",k)
			p.wac_passenger_id=k
			self.Passenger[k]=p
		end
	end
end

function ENT:StopAllSounds()
	for k, s in pairs(self.Sound) do
		s:Stop()
	end
end

local keyids={
	[WAC_HELI_LEANP]	={7,8},
	[WAC_HELI_LEANY]	={9,10},
	[WAC_HELI_LEANR]	={6,5},
	[WAC_HELI_UPDOWN]	={3,4},
	[WAC_HELI_START]	={2,0},
	[WAC_HELI_FIRE]		={12,0},
	[WAC_HELI_CAM]		={11,0},
	[WAC_HELI_NEXTWEP]	={13,0},
	[WAC_HELI_HOVER]	={14,0},
	[WAC_HELI_EXIT]		={1,0},
	[WAC_HELI_FREEAIM]	={15,0},
	[WAC_HELI_THIRDP]	={16,0},
}

function ENT:RocketAlert()
	if self.startMul > 0.1 then
		local b=false
		for k, v in pairs(ents.FindInSphere(self.Entity:GetPos(), 2000)) do
			if string.find(v:GetClass(), "rpg_missile") or string.find(v:GetClass(), "wac_w_rocket") then
				b=true
				break
			else
				b=false
			end
		end
		if self.Sound.MissileAlert:IsPlaying() then
			if !b then
				self.Sound.MissileAlert:Stop()
			end
		elseif b then
			self.Sound.MissileAlert:Stop()
		end
	end
end

function ENT:Think()
	local crt = CurTime()
	if !self.Lock then
		if self.UpdateSecond<crt then
			if self.Phys and self.Phys:IsValid() then
				self.Phys:Wake()
			end
			if self.Burning then
				self:DamageEngine(0.1)
			end
			if self.CrRotorWash then
				if self.startMul > 0.6 then
					if !self.RotorWash then
						self.RotorWash = ents.Create("env_rotorwash_emitter")
						self.RotorWash:SetPos(self.Entity:GetPos())
						self.RotorWash:SetParent(self.Entity)
						self.RotorWash:Activate()
					end
				else
					if self.RotorWash then
						self.RotorWash:Remove()
						self.RotorWash = nil
					end
				end
			end
			self:RocketAlert()
			if self.Smoke then
				self.Smoke:SetKeyValue("renderamt", tostring(math.Clamp(self.startMul*170, 0, 200)))
				self.Smoke:SetKeyValue("Speed", tostring(50+self.startMul*50))
				self.Smoke:SetKeyValue("JetLength", tostring(50+self.startMul*50))
			end
			self:UpdateSeats()
			self.UpdateSecond = crt+0.1
		end
		if self:GetNWFloat("uptime") != math.Clamp(self.startMul,0,150) then
			self:SetNWFloat("uptime", math.Clamp(self.startMul,0,150))
		end
		if self:GetNWFloat("up") != self.upMul then
			self:SetNWFloat("up", self.upMul)
		end
		if self.TopRotor and self.TopRotor:WaterLevel() > 0 then
			self:DamageEngine(0.5)
		end
		for k, t in pairs(self.SeatsT) do
			local p=self.Passenger[k]
			if p and p:IsValid() and p:InVehicle() and p:GetVehicle().BigOne then
				if k==1 then
					if self:GetPLControl(p,WAC_HELI_START,0,true)>0 and self.StartTime<CurTime() then
						self:SwitchState()
						self.StartTime=CurTime()+1
					end
					if self:GetPLControl(p,WAC_HELI_HOVER,0,true)>0 and self.HoverTime<CurTime() then
						self:ToggleHover()
						self.HoverTime=CurTime()+1
					end
				end
				if self:GetPLControl(p,WAC_HELI_THIRDP,0,true,1!=k)>0 and (!p.wac_thirdp_next or p.wac_thirdp_next<crt) then
					local b=p:GetNWBool("wac_thirdp")
					p:SetNWBool("wac_thirdp",!b)
					p.wac_thirdp_next=crt+0.5
					p:ConCommand("gmod_vehicle_viewmode "..(b and 0 or 1))
				end
				if self:GetPLControl(p,WAC_HELI_CAM,0,true, 1!=k)>0 and (!p.NextCamSwitch or p.NextCamSwitch < crt) then
					p:SetNWBool("docam", !p:GetNWBool("docam"))
					p:SetNWBool("wac_mouse_seatinput", k==1 and p:GetNWBool("docam") or false)
					p.NextCamSwitch=crt+0.5
				end
				if t.wep then
					if self:GetPLControl(p,WAC_HELI_NEXTWEP,0,true, 1!=k)>0 and t.wep_next<CurTime() then
						self:NextWeapon(t, k, p)
					end
					if self:GetPLControl(p,WAC_HELI_FIRE,0,true, 1!=k)>0 then
						t.wep[t.wep_act].func(self.Entity, t.wep[t.wep_act], p)
						self:SetNWFloat("seat_"..k.."_"..t.wep_act.."_nextshot", t.wep[t.wep_act].NextShoot)
						self:SetNWFloat("seat_"..k.."_"..t.wep_act.."_lastshot", t.wep[t.wep_act].LastShot)
						self:SetNWInt("seat_"..k.."_"..t.wep_act.."_ammo", t.wep[t.wep_act].Ammo)
					end
					if t.wep[t.wep_act].Think then t.wep[t.wep_act].Think(self.Entity, t.wep[t.wep_act], p) end
				end
			end
		end
	end
	self:NextThink(crt)
	return true
end

function ENT:SendKey(p,k,b)
	if k==keyids[WAC_HELI_EXIT][1] then
		self:EjectPassenger(p)
		return
	end
	p.HeliKeysDown[k]=b
end

function ENT:GetPLControl(pl, id, cur, static, nopilot)
	local mul = tonumber(pl:GetInfo("wac_cl_heli_mul") or 1)*WAC.Helicopter.CVars.PHM:GetFloat()
	local tempJoyVal=0
	if !nopilot and pl:GetInfo("wac_cl_heli_usejoystick")=="1" and joystick then
		tempJoyVal=joystick.Get(pl, "wac_heli_"..id)
		tempJoyVal=(type(tempJoyVal)=="number")and(tempJoyVal)or((tempJoyVal==true)and(1)or(0))
		local addiv=tempJoyVal/127.5-1
		if static then return addiv end
		return math.Clamp(math.pow(addiv, 3)*mul, -1, 1)
	else
		local swap=pl:GetInfo("wac_cl_heli_mouse_swap")
		if (id==WAC_HELI_LEANP or (swap=="1"and id==WAC_HELI_LEANR) or (swap=="0" and id==WAC_HELI_LEANY)) and tonumber(pl:GetInfo("wac_cl_heli_mouse"))==1 then
			if self.Passenger[1].HeliKeysDown[keyids[WAC_HELI_FREEAIM][1]] then return 0 end
			local v=self:WorldToLocal(self:GetPos()+pl:GetAimVector())
			if id==WAC_HELI_LEANP then
				local m=(pl:GetInfo("wac_cl_heli_mouse_invert_pitch")=="1" and -1 or 1)
				return math.Clamp(v.z*mul*-m*10,-1,1)
			else
				if swap=="1" then mul=mul*-1 end
				local m=(pl:GetInfo("wac_cl_heli_mouse_invert_yawroll")=="1" and -1 or 1)
				return math.Clamp(v.y*mul*m*10,-1,1)
			end
		else
			if pl.HeliKeysDown and pl.HeliKeysDown[keyids[id][1]] then
				return math.Clamp(cur+0.03*mul, -1, 1)
			elseif pl.HeliKeysDown and pl.HeliKeysDown[keyids[id][2]] then
				return math.Clamp(cur-0.03*mul, -1, 1)
			end
			if static then
				return cur - cur/7
			end
		end
	end
	return 0
end

function ENT:HasPassenger()
	for k, p in pairs(self.Passenger) do
		if p and p:IsValid() then
			return true
		end
	end
end

function ENT:SetOn(b)
	if b then
		if self.Active then return end
		self.Active = true
	elseif self.Active then
		self.Active=false
	end
	self:SetNWBool("active", self.Active)
end

function ENT:SwitchState()
	self:SetOn(!self.Active)
end

function ENT:ToggleHover()
	self.DoHover=!self.DoHover
	self:SetNWBool("hover",self.DoHover)
end

function ENT:CalculateHover(ph,pos,vel,ang)
	if self.DoHover then
		local v=self:WorldToLocal(pos+vel)
		local av=ph:GetAngleVelocity()
		return{
			p=0,
			r=math.Clamp(-ang.r*0-av.x*0.8+v.y*0.045,-0.6,0.6)
		}
		/*
		if !self.EasyMode then
			return{
				p=math.Clamp(-ang.p*0.6-av.y*0.6-v.x*0.025,-0.65,0.65),
				r=math.Clamp(-ang.r*0.6-av.x*0.6+v.y*0.025,-0.65,0.65)
			}
		else
			return{
				p=math.Clamp(-ang.p*0.3-av.y*0.1-v.x*0.005,-0.1,0.1),
				r=math.Clamp(-ang.r*0.6-av.x*0.8+v.y*0.045,-0.6,0.6)
			}
		end
		*/
	else
		return {p=0,r=0}
	end
end

--[###########]
--[###] DAMAGE
--[###########]

function ENT:PhysicsCollide(cdat, phys)
	if cdat.DeltaTime > 0.5 then
		local mass = cdat.HitObject:GetMass()
		if cdat.HitEntity:GetClass() == "worldspawn" then
			mass = 1000
		end
		local dmg = (cdat.Speed*cdat.Speed*math.Clamp(mass, 0, 1000))/8000000
		if !dmg then return end
		self:TakeDamage(dmg*15)
		if dmg > 2 then
			self.Entity:EmitSound("vehicles/v8/vehicle_impact_heavy"..math.random(1,4)..".wav")
			local lasta=(self.LastDamageTaken<CurTime()+6 and self.LastAttacker or self.Entity)
			for k, p in pairs(self.Passenger) do
				if p and p:IsValid() then
					p:TakeDamage(dmg/5, lasta, self.Entity)
				end
			end
		end
	end
end

function ENT:DamageSmallRotor(amt)
	if amt < 1 then return end
	self.Entity:EmitSound("physics/metal/metal_box_impact_bullet"..math.random(1,3)..".wav", math.Clamp(amt*40,20,200))
	if self.BackRotor and self.BackRotor:IsValid() then
		self.BackRotor.fHealth = self.BackRotor.fHealth - amt
		self.BackRotor.Phys:AddAngleVelocity(Vector(0,-amt*40,0))
		if self.BackRotor.fHealth < 0 then
			self:KillBackRotor()
			if !self.Sound.CrashAlarm:IsPlaying() then
				self.Sound.CrashAlarm:Play()
			end
		elseif self.BackRotor.fHealth < 50 and !self.Sound.MinorAlarm:IsPlaying() then
			self.Sound.MinorAlarm:Play()
		end
		if self.BackRotor then
			self:SetNWFloat("rotorhealth", self.BackRotor.fHealth)
		else
			self:SetNWFloat("rotorhealth", -1)
		end
		self:DamageEngine(amt/2)
	end
end

function ENT:KillBackRotor()
	if !self.BackRotor then return end
	local e = ents.Create("prop_physics")
	e:SetAngles(self.BackRotor:GetAngles())
	e:SetPos(self.BackRotor:GetPos())
	e:SetModel(self.BackRotor:GetModel())
	e:Spawn()
	e:SetVelocity(self.BackRotor:GetUp()*300)
	e:GetPhysicsObject():AddAngleVelocity(self:GetPhysicsObject():GetAngleVelocity())
	self.BackRotor:Remove()
	self.BackRotor=nil
	timer.Simple(10, function()
		if e and e:IsValid() then
			e:Remove()
		end
	end)
end

function ENT:DamageBigRotor(amt)
	if amt < 1 then return end
	self.Entity:EmitSound("physics/metal/metal_box_impact_bullet"..math.random(1,3)..".wav", math.Clamp(amt*40,0,100))
	if self.TopRotor and self.TopRotor:IsValid() then
		self.TopRotor.fHealth = self.TopRotor.fHealth - amt
		self.TopRotor.Phys:AddAngleVelocity((self.TopRotor.Phys:GetAngleVelocity()*-amt)*0.001)
		if self.TopRotor.fHealth < 0 then
			self:KillTopRotor()
			if !self.Sound.CrashAlarm:IsPlaying() then
				self.Sound.CrashAlarm:Play()
			end
		elseif self.TopRotor.fHealth < 50 and !self.Sound.MinorAlarm:IsPlaying() then
			self.Sound.MinorAlarm:Play()
		end
		if amt>5 then
			self:SetOn(false)
		end
		if self.TopRotor then
			self:SetNWFloat("rotorhealth", self.TopRotor.fHealth)
		else
			self:SetNWFloat("rotorhealth", -1)
		end
		self:DamageEngine(amt/2)
	end
end

function ENT:KillTopRotor()
	if !self.TopRotor then return end
	local e = ents.Create("prop_physics")
	e:SetModel(self.RotorModel)
	e:SetPos(self.TopRotor:GetPos())
	e:SetAngles(self.TopRotor:GetAngles())
	e:Spawn()
	self:SetNWFloat("up",0)
	self:SetNWFloat("uptime",0)
	self.startMul=0
	local ph=e:GetPhysicsObject()
	e.wac_ignore=true
	if ph:IsValid() then
		ph:SetMass(1000)
		ph:AddAngleVelocity(self.TopRotor.Phys:GetAngleVelocity())
		ph:SetVelocity(self.TopRotor.Phys:GetAngleVelocity():Length()*self.TopRotor:GetUp()*0.5 + self.TopRotor:GetVelocity())
	end
	self.TopRotor:Remove()
	self.TopRotor = nil
	e:SetNotSolid(true)
	timer.Simple(15, function()
		if !e or !e:IsValid() then return end
		e:Remove()
	end)
end
--[###] Rotor Damage


function ENT:OnTakeDamage(dmg)
	if !dmg:IsExplosionDamage() then
		dmg:ScaleDamage(0.25)
	end
	local rdmg = dmg:GetDamage()
	self:DamageEngine(rdmg/3)
	local pos=self:WorldToLocal(dmg:GetDamagePosition())
	if pos:Distance(self.TopRotorPos)<40 then
		self:DamageBigRotor(rdmg/15)	
	end
	if pos:Distance(self.BackRotorPos)<70 then
		self:DamageSmallRotor(rdmg/2)
	end
	self.LastAttacker=dmg:GetAttacker()
	self.LastDamageTaken=CurTime()
	self:TakePhysicsDamage(dmg)
end

function ENT:DamageEngine(amt)
	self.engineHealth = self.engineHealth - amt
	if self.engineHealth < 50  then
		if !self.Sound.MinorAlarm:IsPlaying() then
			self.Sound.MinorAlarm:Play()
		end
		if !self.Smoke and self.engineHealth>0 then
			self.Smoke = self:CreateSmoke()
		end
		if self.engineHealth < 20 then
			if !self.Sound.LowHealth:IsPlaying() then
				local fire = ents.Create("env_fire_trail")
				fire:SetPos(self:LocalToWorld(self.FirePos))
				fire:Spawn()
				fire:SetParent(self.Entity)
				self.Burning = true
				self.Sound.LowHealth:Play()
			end
			if self.engineHealth < 0 and !self.Lock then
				self.Lock = true
				local lasta=(self.LastDamageTaken<CurTime()+6 and self.LastAttacker or self.Entity)
				for k, p in pairs(self.Passenger) do
					if p and p:IsValid() then
						p:TakeDamage(p:Health() + 20, lasta, self.Entity)
					end
				end
				for k,v in pairs(self.Seats) do
					v:Remove()
				end
				self.Passenger={}
				self:StopAllSounds()
				self.IgnoreDamage = false
				local effectdata = EffectData()
				effectdata:SetStart( self.Entity:GetPos())
				effectdata:SetOrigin( self.Entity:GetPos())
				effectdata:SetScale( 1 )
				util.Effect("Explosion", effectdata)
				util.Effect("HelicopterMegaBomb", effectdata)
				util.Effect("cball_explode", effectdata)
				util.BlastDamage(self.Entity, self.Entity, self.Entity:GetPos(), 300, 300)
				self:SetOn(false)
				if self.Smoke then
					self.Smoke:Remove()
					self.Smoke=nil
				end
				if self.RotorWash then
					self.RotorWash:Remove()
					self.RotorWash=nil
				end
				self:SetNWBool("locked", true)
			end
		end
	end
	if self.Smoke then
		local rcol = math.Clamp(self.engineHealth*3.4, 0, 170)
		self.Smoke:SetKeyValue("rendercolor", rcol.." "..rcol.." "..rcol)
	end
	self:SetNWFloat("health", self.engineHealth)
end

function ENT:CreateSmoke()
	local smoke = ents.Create("env_smokestack")
	smoke:SetPos(self:LocalToWorld(self.SmokePos))
	smoke:SetAngles(self:GetAngles()+Angle(-90,0,0))
	smoke:SetKeyValue("InitialState", "1")
	smoke:SetKeyValue("WindAngle", "0 0 0")
	smoke:SetKeyValue("WindSpeed", "0")
	smoke:SetKeyValue("rendercolor", "170 170 170")
	smoke:SetKeyValue("renderamt", "170")
	smoke:SetKeyValue("SmokeMaterial", "particle/smokesprites_0001.vmt")
	smoke:SetKeyValue("BaseSpread", "2")
	smoke:SetKeyValue("SpreadSpeed", "2")
	smoke:SetKeyValue("Speed", "50")
	smoke:SetKeyValue("StartSize", "10")
	smoke:SetKeyValue("EndSize", "50")
	smoke:SetKeyValue("roll", "10")
	smoke:SetKeyValue("Rate", "15")
	smoke:SetKeyValue("JetLength", "50")
	smoke:SetKeyValue("twist", "5")
	smoke:Spawn()
	smoke:SetParent(self.Entity)
	smoke:Activate()
	return smoke
end

function ENT:AddOnRemove(f)
	if type(f)=="function" then
		table.insert(self.OnRemoveFunctions,f)	
	elseif type(f)=="Entity" or type(f)=="Vehicle" then
		table.insert(self.OnRemoveEntities,f)
	end
end

function ENT:OnRemove()
	self:StopAllSounds()
	for _,p in pairs(self.Passenger) do
		if ValidEntity(p) then
			p:SetNWInt("wac_passenger_id",0)
			p.wac_passenger_id=0
		end
	end
	for _,f in pairs(self.OnRemoveFunctions) do
		f()
	end
	for _,e in pairs(self.OnRemoveEntities) do
		if ValidEntity(e) then e:Remove() end
	end
end
